<template>
  <div id="root1">
    <div class="add-container">
      <div class="add-wrap">
        <AddPointHeader></AddPointHeader>
        <AddPointList :points="points"></AddPointList>
        <AddPointFooter :points="points"></AddPointFooter>
      </div>
    </div>
  </div>
  <div id="root2">
    <div class="add-container">
      <div class="add-wrap">
        <AddSideHeader></AddSideHeader>
        <AddSideList :sides="sides"></AddSideList>
        <AddSideFooter :sides="sides"></AddSideFooter>
      </div>
    </div>
  </div>
  <div id="root3">
    <Txt :points="points" :sides="sides"></Txt>
    <Graph :points="points" :sides="sides" :allSorts="allSorts"></Graph>
  </div>
  <div id="root4" :class="{ active: isActive }">
    <div class="add-container">
      <div class="add-wrap">
        <SortResult :points="points" :sides="sides" :allSorts="allSorts" :result_length="result_length"></SortResult>
      </div>
    </div>
  </div>
</template>

<script>
import bus from '@/utils/bus';
import { ref, onMounted, onBeforeUnmount, watch } from 'vue';
import { nanoid } from 'nanoid';
import Graph from './components/Graph.vue';
import AddPointHeader from './components/AddPointHeader.vue';
import AddPointList from './components/AddPointList.vue';
import AddPointFooter from './components/AddPointFooter.vue';
import AddSideHeader from './components/AddSideHeader.vue';
import AddSideFooter from './components/AddSideFooter.vue';
import AddSideList from './components/AddSideList.vue';
import SortResult from './components/SortResult.vue';
import Txt from './components/Txt.vue';

export default {
  name: 'App',
  components: {
    Graph,
    AddPointHeader,
    AddPointList,
    AddPointFooter,
    AddSideHeader,
    AddSideList,
    AddSideFooter,
    SortResult,
    Txt
  },
  setup() {
    const points = ref(JSON.parse(localStorage.getItem('points')) || []);
    const sides = ref(JSON.parse(localStorage.getItem('sides')) || []);
    const allSorts = ref(JSON.parse(localStorage.getItem('allSorts')) || []);
    const isActive = ref(false);
    const result_length = ref(0);

    //添加一个点
    function addpoint_list(point) {
      const ifpoint = point
      let addnow = 0
      points.value.forEach((point) => {
        if (point.title === ifpoint.title) {
          addnow++;
        }
      })
      if (!addnow) {
        points.value.unshift(ifpoint)
      }
      else {
        alert('想要添加的点有重复，请重新输入')
      }
    }

    //勾选或取消勾选一个点
    function checkpoint(id) {
      points.value.forEach((point) => {
        if (point.id === id) point.choose = !point.choose
      })
    }

    //删除一个点,同时删除附属边
    function deletepoint(id) {
      let titles1 = []
      let titles2 = []
      let dele_title
      for (let i = 0; i < points.value.length; i++) {
        if (points.value[i].id === id) {
          dele_title = points.value[i].title //获取要删除的点的title
        }
      }
      for (let k = 0; k < points.value.length; k++) {
        for (let i = 0; i < points.value[k].linkpoint.length; i++) {
          if (points.value[k].linkpoint[i] === dele_title) {
            let title1 = '<' + dele_title + ',' + points.value[k].title + '>'
            titles1.push(title1) //以其为起始点的边
          }
        }
        if (points.value[k].title === dele_title) {
          for (let j = 0; j < points.value[k].linkpoint.length; j++) {
            let title2 = '<' + points.value[k].linkpoint[j] + ',' + points.value[k].title + '>'
            titles2.push(title2) //以其为终点的边
          }
        }
      }
      const merged = titles1.concat(titles2);
      sides.value.forEach((side) => {
        for (let i = 0; i < merged.length; i++) {
          if (side.title === merged[i]) {
            deleteside(side.id)
          }
        }
      })
      points.value = points.value.filter((point) => {
        return point.id !== id
      })
    }

    //全选或全不选
    function checkallpoint(status) {
      if (status) {
        points.value.forEach((point) => {
          point.choose = true
        })
      }
      else {
        points.value.forEach((point) => {
          point.choose = false
        })
      }
    }

    //清除所有选中的点
    function clearchoosepoint() {
      points.value.forEach((point) => {
        if (point.choose) {
          deletepoint(point.id)
        }
      })
    }

    //添加一条边
    function addside_list(side) {
      const ifside = side
      let addnow = 0
      sides.value.forEach((side) => {
        if (side.title === ifside.title) {
          addnow++;
        }
      })
      if (!addnow) {
        const regex = /<([^,]+),([^>]+)>/g;
        const matches = regex.exec(side.title);
        const start = matches[1];
        const end = matches[2];
        points.value.forEach((point) => {
          if (point.title === end) {
            point.indegree++;
            point.linkpoint.push(start);
          }
        })
        sides.value.unshift(ifside)
      }
      else {
        alert('想要添加的边有重复，请重新输入')
      }
    }

    //勾选或取消勾选一条边
    function checkside(id) {
      sides.value.forEach((side) => {
        if (side.id === id) side.choose = !side.choose
      })
    }

    //删除一条边,同时更新删除后的相关点的入度和邻接数组
    function deleteside(id) {
      const regex = /<([^,]+),([^>]+)>/g;
      let matches;
      sides.value.forEach((side) => {
        if (side.id === id) {
          matches = regex.exec(side.title);
        }
      })
      const start = matches[1];
      const end = matches[2];
      points.value.forEach((point) => {
        if (point.title === end) {
          point.indegree--;
          for (let i = 0; i < point.linkpoint.length; i++) {
            if (point.linkpoint[i] === start) {
              point.linkpoint.splice(i, 1)
            }
          }
        }
      })
      sides.value = sides.value.filter((side) => {
        return side.id !== id
      })
    }

    //全选或全不选
    function checkallside(status) {
      if (status) {
        sides.value.forEach((side) => {
          side.choose = true
        })
      }
      else {
        sides.value.forEach((side) => {
          side.choose = false
        })
      }
    }

    //清除所有选中的边
    function clearchooseside() {
      sides.value.forEach((side) => {
        if (side.choose) {
          deleteside(side.id);
        }
      })
    }

    //判断哪个点被激活
    function checkpointactive(id) {
      points.value.forEach((point) => {
        if (point.id !== id) {
          point.isactive = false
        }
      })
    }

    //在图上的两点间画一条线
    function drawSide(psp) {
      let name1;
      let name2;
      points.value.forEach((point) => {
        if (point.id === psp[0]) {
          name1 = point.title;
        }
      })
      points.value.forEach((point) => {
        if (point.id === psp[1]) {
          name2 = point.title
        }
      })
      const side = { id: nanoid(), title: `<${name1},${name2}>`, choose: false };
      addside_list(side);
    }

    //判断导入的文件要添加的边所包含的点是否有重复，如果有，则只更新重复点的入度和邻接数组。
    function addpoint_txt(point) {
      const ifpoint = point
      let addnow = 0
      points.value.forEach((point) => {
        if (point.title === ifpoint.title) {
          addnow++;
        }
      })
      if (!addnow) {
        points.value.unshift(ifpoint)
      }
    }

    //判断导入的文件要添加的边是否有重复，如果有，则只添加新的边。
    function addside_txt(side) {
      const ifside = side
      let addnow = 0
      sides.value.forEach((side) => {
        if (side.title === ifside.title) {
          addnow++;
        }
      })
      if (!addnow) {
        const regex = /<([^,]+),([^>]+)>/g;
        const matches = regex.exec(side.title);
        const start = matches[1];
        const end = matches[2];
        points.value.forEach((point) => {
          if (point.title === end) {
            point.indegree++;
            point.linkpoint.push(start);
          }
        })
        sides.value.unshift(ifside)
      }
    }

    function changeclass() {
      isActive.value = !isActive.value
    }

    //拓扑排序算法
    function allTopologicalSorts(graph) {
      const visited = new Set();
      const stack = [];
      const results = [];

      function backtrack() {
        let isAllVisited = true;

        for (let vertex in graph) {
          if (!visited.has(vertex) && isIndependent(vertex)) {
            visited.add(vertex);
            stack.push(vertex);
            backtrack();
            visited.delete(vertex);
            stack.pop();

            isAllVisited = false;
          }
        }

        if (isAllVisited) {
          results.push([...stack]);
        }
      }

      function isIndependent(vertex) {
        for (let v in graph) {
          if (graph[v].includes(vertex) && !visited.has(v)) {
            return false;
          }
        }
        return true;
      }

      backtrack();

      let tuopus = [];
      for (let l = 0; l < results.length; l++) {
        let sequences = '';
        for (let m = 0; m < points.value.length; m++) {
          if (m !== points.value.length - 1) {
            sequences += results[l][m] + '-> ';
          }
          else {
            sequences += results[l][m];
          }
        }
        const tuopu = { id: nanoid(), sequence: sequences }
        tuopus.push(tuopu);
      }

      return tuopus;
    }

    function delete_result(graph) {
      if (sides.value.length === 0) {
        return
      }
      const all = allTopologicalSorts(graph);
      result_length.value = all.length
      allSorts.value.splice(0, allSorts.value.length)
      if (all.length < 20) {
        for (let i = 0; i < all.length; i++) {
          allSorts.value[i] = all[i]
        }
      }
      else {
        for (let i = 0; i < 20; i++) {
          allSorts.value[i] = all[i]
        }
      }
    }

    onMounted(() => {
      bus.on('addpoint_list', addpoint_list)
      bus.on('checkpoint', checkpoint)
      bus.on('deletepoint', deletepoint)
      bus.on('checkallpoint', checkallpoint)
      bus.on('clearchoosepoint', clearchoosepoint)

      bus.on('addside_list', addside_list)
      bus.on('checkside', checkside)
      bus.on('deleteside', deleteside)
      bus.on('checkallside', checkallside)
      bus.on('clearchooseside', clearchooseside)

      bus.on('checkpointactive', checkpointactive)
      bus.on('drawSide', drawSide)

      bus.on('addpoint_txt', addpoint_txt)
      bus.on('addside_txt', addside_txt)

      bus.on('changeclass', changeclass)
      bus.on('delete_result', delete_result)
    })

    onBeforeUnmount(() => {
      bus.off('*')
    })

    watch(
      () => sides.value,
      (value) => {
        localStorage.setItem('sides', JSON.stringify(value))
      }, {
      deep: true
    }
    )

    watch(
      () => points.value,
      (value) => {
        localStorage.setItem('points', JSON.stringify(value))
      }, {
      deep: true
    }
    )

    watch(
      () => allSorts.value,
      (value) => {
        localStorage.setItem('allSorts', JSON.stringify(value))
      }, {
      deep: true
    }
    )

    return {
      points,
      sides,
      allSorts,
      isActive,
      result_length
    }
  }
}
</script>

<style>
html {
  height: 100%;
  width: 100%;
}

body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  user-select: none;
  overflow: hidden;
  background: linear-gradient(to right, #77c9d4, #57bc90);
  ;
}

#root1 {
  width: 30%;
  height: 38%;
  position: absolute;
  left: 0;
  background: linear-gradient(to right, #b6a19e, #6c648b);
}

#root2 {
  width: 30%;
  height: 38%;
  position: absolute;
  left: 0;
  top: 40%;
  background: linear-gradient(to right, #b6a19e, #6c648b);
}

#root3 {
  width: 67%;
  height: 100%;
  display: flex;
  flex-direction: column;
  position: absolute;
  right: 0;
}

#root4 {
  width: 30%;
  height: 100%;
  position: absolute;
  left: 0;
  top: 90%;
  background: linear-gradient(to right, #b6a19e, #6c648b);
}

.add-container {
  width: 100%;
  height: 100%;
  margin: 0 auto;
}

.add-container .add-wrap {
  box-sizing: border-box;
  height: 100%;
  padding: .625rem;
  border: 1px solid #ddd;
  border-radius: .3125rem;
}

.btn {
  display: inline-block;
  padding: .25rem .75rem;
  margin-bottom: 0;
  font-size: 14px;
  line-height: 1.25rem;
  text-align: center;
  vertical-align: middle;
  cursor: pointer;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
}

.btn-danger {
  color: #fff;
  background-color: #da4f49;
  border: 1px solid #bd362f;
}

.btn-danger:hover {
  color: #fff;
  background-color: #bd362f;
}

.btn:focus {
  outline: none;
}

.active {
  top: 0 !important;
}</style>
